/*
 * NativeRobot.c
 * 
 * Copyright (C) 2007 Francisco J. Hernández
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 */

#include "NativeRobot.h"
#include <jni.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct {
       long x;
       long y;
} POINTAPI;

unsigned short mask_is_pulse = (~(unsigned short)0) - (unsigned short)1;

#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")

/*
Devuelve la posici�n del raton
*/
JNIEXPORT jintArray JNICALL Java_NativeRobot_NativeRobot_getNativeMousePos(JNIEnv *env, jobject o)
{
          POINTAPI a;
          jintArray coor = (*env)->NewIntArray(env, 2 * sizeof(jint));
          GetCursorPos(&a);
          (*env)->SetIntArrayRegion(env, (jintArray)coor, 0, 2, (jint *)&(a));
          return coor;
}

/*
Si el boton del rat�n cuyo codigo es pasado esta pulsado, devuelve 1
*/
JNIEXPORT jint JNICALL Java_NativeRobot_NativeRobot_isNativeButtonPressed (JNIEnv *env, jobject o, jint codeButton)
{
          return (GetAsyncKeyState(codeButton) & mask_is_pulse);
}

/*
 * Class:     NativeRobot
 * Method:    isNativeKeyPressed
 * Signature: Responde si una tecla esta pulsada o no
 */
JNIEXPORT jint JNICALL Java_NativeRobot_NativeRobot_isNativeKeyPressed(JNIEnv *env, jobject o, jint codeKey)
{
          return (GetAsyncKeyState(codeKey) & mask_is_pulse);
}

/*
 * Class:     NativeRobot
 * Method:    nativeGetPixelColor
 * Signature: Obtiene el color de un punto de pantalla
 */
JNIEXPORT jintArray JNICALL Java_NativeRobot_NativeRobot_nativeGetPixelColor(JNIEnv *env, jobject o, jint handle, jint x, jint y) {
   HDC h = GetWindowDC(handle);
   jintArray color = (*env)->NewIntArray(env, 3);
   COLORREF col = GetPixel(h, x, y);
   (*env)->SetIntArrayRegion(env, (jintArray)color, 0, 2, (jint *)&(col));
   return color;
}

/*
 * Class:     NativeRobot
 * Method:    nativeKeyPress
 * Signature: Simula una pulsaci�n de tecla
 */
JNIEXPORT jboolean JNICALL Java_NativeRobot_NativeRobot_nativeKeyPress(JNIEnv *env, jobject o, jint handle, jint keyCode)
{
          return PostMessage((HWND)handle, WM_KEYDOWN, keyCode, 0);
}

/*
 * Class:     NativeRobot
 * Method:    nativeKeyRelease
 * Signature: Simula la liberaci�n de una tecla
 */
JNIEXPORT jboolean JNICALL Java_NativeRobot_NativeRobot_nativeKeyRelease(JNIEnv *env, jobject o, jint handle, jint keyCode) {
          return PostMessage((HWND)handle, WM_KEYUP, keyCode, 0);
}

#define YNUMBITS 16
int xMouse = 0;
int yMouse = 0;
int button = 0;

/*
 * Class:     NativeRobot
 * Method:    nativeMouseMove
 * Signature: Realiza un movimiento del rat�n a un punto de la ventana
 */
JNIEXPORT jboolean JNICALL Java_NativeRobot_NativeRobot_nativeMouseMove(JNIEnv *env, jobject o, jint handle, jint x, jint y) {
          xMouse = x;
          yMouse = y << YNUMBITS;
          
          return PostMessage((HWND)handle, WM_MOUSEMOVE, button, yMouse + xMouse);
}

/*
 * Class:     NativeRobot
 * Method:    nativeLeftMousePress
 * Signature: Simula la pulsaci�n del boton izquierdo del rat�n sobre la ventana adecuada
 */
JNIEXPORT jboolean JNICALL Java_NativeRobot_NativeRobot_nativeLeftMousePress(JNIEnv *env, jobject o, jint handle) {
          button |= MK_LBUTTON;
          return PostMessage((HWND)handle, WM_LBUTTONDOWN, button, yMouse + xMouse);
}

/*
 * Class:     NativeRobot
 * Method:    nativeMiddleMousePress
 * Signature: Simula la pulsaci�n del boton medio del rat�n sobre la ventana adecuada
 */
JNIEXPORT jboolean JNICALL Java_NativeRobot_NativeRobot_nativeMiddleMousePress(JNIEnv *env, jobject o, jint handle) {
          button |= MK_MBUTTON;
          return PostMessage((HWND)handle, WM_MBUTTONDOWN, button, yMouse + xMouse);
}

/*
 * Class:     NativeRobot
 * Method:    nativeRightMousePress
 * Signature: Simula la pulsaci�n del boton derecho del rat�n sobre la ventana adecuada
 */
JNIEXPORT jboolean JNICALL Java_NativeRobot_NativeRobot_nativeRightMousePress(JNIEnv *env, jobject o, jint handle) {
          button |= MK_RBUTTON;
          return PostMessage((HWND)handle, WM_RBUTTONDOWN, button, yMouse + xMouse);
}

/*
 * Class:     NativeRobot
 * Method:    nativeLeftMouseRelease
 * Signature: Simula la liberaci�n del boton izquierdo del rat�n sobre la ventana adecuada
 */
JNIEXPORT jboolean JNICALL Java_NativeRobot_NativeRobot_nativeLeftMouseRelease(JNIEnv *env, jobject o, jint handle) {
          button &= !MK_LBUTTON;
          return PostMessage((HWND)handle, WM_LBUTTONUP, button, yMouse + xMouse);
}

/*
 * Class:     NativeRobot
 * Method:    nativeMiddleMouseRelease
 * Signature: Simula la liberaci�n del boton medio del rat�n sobre la ventana adecuada
 */
JNIEXPORT jboolean JNICALL Java_NativeRobot_NativeRobot_nativeMiddleMouseRelease(JNIEnv *env, jobject o, jint handle) {
          button &= !MK_MBUTTON;
          return PostMessage((HWND)handle, WM_MBUTTONUP, button, yMouse + xMouse);
}

/*
 * Class:     NativeRobot
 * Method:    nativeRightMouseRelease
 * Signature: Simula la liberaci�n del boton medio del rat�n sobre la ventana adecuada
 */
JNIEXPORT jboolean JNICALL Java_NativeRobot_NativeRobot_nativeRightMouseRelease(JNIEnv *env, jobject o, jint handle) {
          button &= !MK_RBUTTON;
          return PostMessage((HWND)handle, WM_RBUTTONUP, button, yMouse + xMouse);
}

/*
 * Class:     NativeRobot
 * Method:    getNativeWindowTittle
 * Signature: Devuelve el nombre de una ventana cuyo hwnd se pasa por parametro.
 */
JNIEXPORT jbyteArray JNICALL Java_NativeRobot_NativeRobot_getNativeWindowTittle(JNIEnv *env, jobject o, jint hwnd) {
          int numChar = GetWindowTextLength(hwnd);
          char str[numChar];
          GetWindowText(hwnd, &str, numChar);
          
          jbyteArray nombre = (*env)->NewByteArray(env, numChar);
          (*env)->SetIntArrayRegion(env, (jbyteArray)nombre, 0, numChar, (jbyte *)&(str));
          
          return nombre;
}

int cont = 0;           // Primer elemento vacio del vector de descriptores de ventana
int tamMax = 500;       // Maximo numero de elementos en el vector de descriptores de ventana
HWND *h = NULL;         // Vector de descriptores de ventana

/*
* Almacena el descriptor de ventana pasado por parametros desde EnumWindows
* Si falta memoria redimensiona el vector.
*/
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)
{
    h[cont++] = hwnd;
    if (tamMax <= cont) 
    {
       tamMax *= 2;
       h = (HWND *)realloc(h, tamMax * sizeof(HWND));
    }
    return TRUE;
}

/*
 * Class:     NativeRobot
 * Method:    getNativeAllWindowHandle
 * Signature: Devuelve todos los descriptores de ventana que encuentra la funci�n EnumWindows
 */
JNIEXPORT jintArray JNICALL Java_NativeRobot_NativeRobot_getNativeAllWindowHandle(JNIEnv *env, jobject o) 
{
          LPARAM a;
          h = (HWND *)malloc(tamMax * sizeof(HWND));
          if (h == NULL)
             return NULL;
          EnumWindows(EnumChildProc, (LPARAM)&a);
          jintArray handles = (*env)->NewIntArray(env, cont);
          (*env)->SetIntArrayRegion(env, (jintArray)handles, 0, cont, (jint *)(h));
          free(h);
          return handles;
}

/*
 * Class:     NativeRobot
 * Method:    getNativeMousePosHandle
 * Signature: (I)[I
 */
JNIEXPORT jintArray JNICALL Java_NativeRobot_NativeRobot_getNativeMousePosHandle(JNIEnv *env, jobject o, jint handle) {
   int pos[2] = {xMouse, yMouse};
   jintArray vPos = (*env)->NewIntArray(env, 2);
   (*env)->SetIntArrayRegion(env, (jintArray)vPos, 0, 2, (jint *)&(pos));
   return vPos;
}

BOOL APIENTRY DllMain (HINSTANCE hInst     /* Library instance handle. */ ,
                       DWORD reason        /* Reason this function is being called. */ ,
                       LPVOID reserved     /* Not used. */ )
{
    switch (reason)
    {
      case DLL_PROCESS_ATTACH:
        break;

      case DLL_PROCESS_DETACH:
        break;

      case DLL_THREAD_ATTACH:
        break;

      case DLL_THREAD_DETACH:
        break;
    }

    /* Returns TRUE on success, FALSE on failure */
    return TRUE;
}
